package com.google.code.gronono.commons;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

/**
 * Classe utilitaire permettant la manipulation de l'API Réflection.
 *
 * @author Arnaud BRUNET
 */
public class ReflectUtils {
	/** Logger. */
	private static final Logger logger =  Logger.getLogger(ReflectUtils.class);

	/**
	 * Cette classe est une classe utilitaire.
	 * Son constructeur ne doit jamais être appelé.
	 * @throws UnsupportedOperationException Toujours.
	 */
	private ReflectUtils() {
		throw new UnsupportedOperationException();
	}

	/**
	 * Change la valeur du champ spécifié par <code>field</code> de l'objet spécifié par <code>object</code> par la valeur <code>value</code>.<br/>
	 * Si un setter existe pour cette objet, cette méthode l'appel. Sinon, elle change directement la valeur.
	 * @param object L'objet dont le champ doit être modifié.
	 * @param field Le champ de l'objet dont la valeur va être changée.
	 * @param value La nouvelle valeur du champ.
	 * @throws IllegalStateException En cas d'erreur {@link IllegalAccessException}.
	 *
	 * @see Field#set(Object, Object) pour plus d'information.
	 */
	public static void setField(final Object object, final Field field, final Object value) {
		logger.trace("Set field "+ field.getName() + " with value " + value);

		boolean accessible = field.isAccessible();
		if (!accessible) {
			field.setAccessible(true);
		}
		try {
			field.set(object, value);
		} catch (final IllegalAccessException e) {
			throw new IllegalStateException(e);
		} finally {
			if (!accessible) {
				field.setAccessible(accessible);
			}
		}
	}

	/**
	 * Donne les champs annotés par l'annotation spécifié par <code>annotation</code> de la classe spécifiée par <code>clazz</code>.
	 * @param clazz La classe dont on veut les champs annotés.
	 * @param annotation L'annotation dont doit être annotés les champs.
	 * @return Un tableau contenant les champs annotés par <code>annotation</code> de la classe <code>clazz</code>.
	 */
	public static Field [] getAnnotatedFields(final Class<?> clazz, final Class<? extends Annotation> annotation) {
		final List<Field> fields = new ArrayList<Field>();
		for (final Field field : clazz.getDeclaredFields()) {
			if (field.isAnnotationPresent(annotation)) {
				fields.add(field);
			}
		}

		return fields.toArray(new Field[fields.size()]);
	}

}
